package net.cl.tools.json;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import net.cl.Constants;
import net.cl.tools.HttpProxy;

public class JsonObject implements Json {

	private static final long serialVersionUID = -8348762514227441522L;
	private Map<String, Json> jsonMap;
	private int level = 1;

	public JsonObject() {
		jsonMap = new LinkedHashMap<String, Json>();
	}

	protected JsonObject(String json) {
		this();
	}

	public static JsonObject fromString(String json) {
		JsonBuilder jb = new JsonBuilder(json);
		jb.validate();
		return (JsonObject) jb.build();
	}
	
	public static JsonObject connect(String url) {
		HttpProxy p = new HttpProxy(url);
		try {
			return fromString(p.get());
		} catch (IOException e) {
			throw new JsonException("Connect to specified url error : " + url, e);
		}
	}

	public JsonObject getJsonObject(String key) {
		return (JsonObject) get(key);
	}

	public JsonArray getJsonArray(String key) {
		return (JsonArray) get(key);
	}

	private JsonElement getJsonElement(String key) {
		return (JsonElement) get(key);
	}

	public boolean containsKey(String key) {
		return jsonMap.containsKey(key);
	}

	public String getString(String key) {
		return getJsonElement(key).getValue();
	}

	public long getLong(String key) {
		return getJsonElement(key).toLong();
	}

	public double getDouble(String key) {
		return getJsonElement(key).toDouble();
	}

	public boolean getBoolean(String key) {
		return getJsonElement(key).toBoolean();
	}

	public JsonObject putJson(String key, Json json) {
		if (json instanceof JsonObject)
			((JsonObject) json).setLevel(level + 1);
		else if (json instanceof JsonArray)
			((JsonArray) json).setLevel(level + 1);
		jsonMap.put(key, json);
		return this;
	}

	public JsonObject putString(String key, String value) {
		jsonMap.put(key, new JsonElement(value));
		return this;
	}

	public JsonObject putNumber(String key, Number value) {
		jsonMap.put(key, new JsonElement(value));
		return this;
	}

	public JsonObject putBoolean(String key, boolean value) {
		jsonMap.put(key, new JsonElement(value));
		return this;
	}

	public JsonObject putNull(String key) {
		jsonMap.put(key, new JsonElement("null", false));
		return this;
	}

	public JsonObject remove(String key) {
		jsonMap.remove(key);
		return this;
	}

	public int size() {
		return jsonMap.size();
	}

	public String toString() {
		return toString(false);
	}

	public String toTidyString() {
		return toString(true);
	}

	private String toString(boolean tidy) {
		StringBuilder sb = new StringBuilder();
		if (tidy) {
			for (int j = 0; j < level - 1; j++) {
				sb.append("  ");
			}
		}
		sb.append('{');
		if (tidy)
			sb.append(Constants.LINE_SEPARATOR);
		int i = 1;
		for (Entry<String, Json> entry : jsonMap.entrySet()) {
			if (tidy) {
				for (int j = 0; j < level; j++) {
					sb.append("  ");
				}
			}
			Json json = entry.getValue();
			sb.append('"');
			sb.append(entry.getKey());
			sb.append('"');
			sb.append(':');
			if (tidy) {
				if (json instanceof JsonObject) {
					sb.append(Constants.LINE_SEPARATOR);
					sb.append(((JsonObject) json).toTidyString());
				} else if (json instanceof JsonArray) {
					sb.append(Constants.LINE_SEPARATOR);
					sb.append(((JsonArray) json).toTidyString());
				} else
					sb.append(json.toString());
			} else
				sb.append(json.toString());
			if (i < jsonMap.entrySet().size())
				sb.append(',');
			if (tidy)
				sb.append(Constants.LINE_SEPARATOR);
			i++;
		}
		if (tidy) {
			for (int j = 0; j < level - 1; j++) {
				sb.append("  ");
			}
		}
		sb.append('}');
		return sb.toString();
	}

	private Json get(String key) {
		if (containsKey(key))
			return jsonMap.get(key);
		throw new JsonException("The object is not exist. " + key);
	}

	protected void setLevel(int level) {
		this.level = level;
	}

}
